Coverage Report

Created: 2026-03-18 12:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\scloud-dns\scloud-dns\src\dns\resolver\mod.rs
Line
Count
Source
1
pub(crate) mod stub;
2
3
use crate::dns::packet::DNSPacket;
4
use crate::dns::packet::additional::AdditionalSection;
5
use crate::dns::packet::answer::AnswerSection;
6
use crate::dns::packet::authority::AuthoritySection;
7
use crate::dns::packet::question::QuestionSection;
8
use crate::exceptions::SCloudException;
9
10
3
pub(crate) fn check_response_diff(
11
3
    dns_packet: DNSPacket,
12
3
    origin_questions: &[QuestionSection],
13
3
) -> Result<(), SCloudException> {
14
3
    let diff_answers = if !dns_packet.answers.is_empty() {
15
2
        check_answer_diff(origin_questions, &dns_packet.answers)
16
    } else {
17
1
        Ok(())
18
    };
19
20
3
    let diff_authorities = if !dns_packet.authorities.is_empty() {
21
1
        check_authority_diff(origin_questions, &dns_packet.authorities)
22
    } else {
23
2
        Ok(())
24
    };
25
26
3
    let diff_additionals = if !dns_packet.additionals.is_empty() {
27
0
        check_additional_diff(origin_questions, &dns_packet.additionals)
28
    } else {
29
3
        Ok(())
30
    };
31
32
3
    if diff_answers.is_err() || 
diff_authorities2
.
is_err2
() ||
diff_additionals2
.
is_err2
() {
33
1
        return Err(SCloudException::SCLOUD_RESOLVER_RESPONSE_MISMATCH);
34
2
    }
35
36
2
    Ok(())
37
3
}
38
39
/// Check that each question has at least one corresponding record
40
/// in the answer, authority, or additional sections.
41
///
42
/// This prevents responses that do not actually answer the query.
43
///
44
/// # Exemple :
45
/// ```
46
/// use crate::dns::resolver::check_answer_diff;
47
/// use crate::dns::packet::question::QuestionSection;
48
/// use crate::dns::packet::answer::AnswerSection;
49
/// use crate::dns::q_type::DNSRecordType;
50
/// use crate::dns::q_class::DNSClass;
51
///
52
/// let questions = vec![QuestionSection {
53
///     q_name: "example.com".to_string(),
54
///     q_type: DNSRecordType::A,
55
///     q_class: DNSClass::IN,
56
/// }];
57
///
58
/// let answers = vec![AnswerSection {
59
///     q_name: "example.com".to_string(),
60
///     r_type: DNSRecordType::A,
61
///     r_class: DNSClass::IN,
62
///     ttl: 300,
63
///     rdlength: 4,
64
///     rdata: vec![93, 184, 216, 34],
65
/// }];
66
///
67
/// assert!(check_answer_diff(&questions, &answers, &[], &[]).is_ok());
68
/// ```
69
4
pub(crate) fn check_answer_diff(
70
4
    questions: &[QuestionSection],
71
4
    answers: &[AnswerSection],
72
4
) -> Result<(), SCloudException> {
73
4
    for record in answers.iter() {
74
4
        if !questions.iter().any(|q| record.q_name == q.q_name) {
75
2
            return Err(SCloudException::SCLOUD_RESOLVER_ANSWER_QNAME_MISMATCH);
76
2
        }
77
    }
78
2
    Ok(())
79
4
}
80
81
/// Ensure that authority records belong to the same zone
82
/// as the original DNS questions.
83
///
84
/// This prevents out-of-zone NS records.
85
///
86
/// # Exemple :
87
/// ```
88
/// use crate::dns::resolver::check_authority_diff;
89
/// use crate::dns::packet::question::QuestionSection;
90
/// use crate::dns::packet::authority::AuthoritySection;
91
/// use crate::dns::q_type::DNSRecordType;
92
/// use crate::dns::q_class::DNSClass;
93
///
94
/// let questions = vec![QuestionSection {
95
///     q_name: "example.com".to_string(),
96
///     q_type: DNSRecordType::NS,
97
///     q_class: DNSClass::IN,
98
/// }];
99
///
100
/// let authorities = vec![AuthoritySection {
101
///     q_name: "example.com".to_string(),
102
///     q_type: DNSRecordType::NS,
103
///     q_class: DNSClass::IN,
104
///     ttl: 3600,
105
///     ns_name: "ns1.example.com".to_string(),
106
/// }];
107
///
108
/// assert!(check_authority_diff(&questions, &authorities).is_ok());
109
/// ```
110
#[allow(unused)]
111
3
pub(crate) fn check_authority_diff(
112
3
    questions: &[QuestionSection],
113
3
    authorities: &[AuthoritySection],
114
3
) -> Result<(), SCloudException> {
115
3
    for record in authorities.iter() {
116
3
        if !questions.iter().any(|q| record.q_name == q.q_name) {
117
1
            return Err(SCloudException::SCLOUD_RESOLVER_AUTHORITY_QNAME_MISMATCH);
118
2
        }
119
    }
120
2
    Ok(())
121
3
}
122
123
/// Ensure that additional records correspond to the original questions.
124
///
125
/// This is commonly used to validate glue records.
126
///
127
/// # Exemple :
128
/// ```
129
/// use crate::dns::resolver::check_additional_diff;
130
/// use crate::dns::packet::question::QuestionSection;
131
/// use crate::dns::packet::additional::AdditionalSection;
132
/// use crate::dns::q_type::DNSRecordType;
133
/// use crate::dns::q_class::DNSClass;
134
///
135
/// let questions = vec![QuestionSection {
136
///     q_name: "example.com".to_string(),
137
///     q_type: DNSRecordType::A,
138
///     q_class: DNSClass::IN,
139
/// }];
140
///
141
/// let additionals = vec![AdditionalSection {
142
///     q_name: "example.com".to_string(),
143
///     q_type: DNSRecordType::A,
144
///     q_class: DNSClass::IN,
145
///     ttl: 300,
146
///     rdlength: 4,
147
///     rdata: vec![192, 0, 2, 1],
148
/// }];
149
///
150
/// assert!(check_additional_diff(&questions, &additionals).is_ok());
151
/// ```
152
#[allow(unused)]
153
2
pub(crate) fn check_additional_diff(
154
2
    questions: &[QuestionSection],
155
2
    additionals: &[AdditionalSection],
156
2
) -> Result<(), SCloudException> {
157
2
    for record in additionals.iter() {
158
2
        if !questions.iter().any(|q| record.q_name == q.q_name) {
159
1
            return Err(SCloudException::SCLOUD_RESOLVER_ADDITIONNAL_QNAME_MISMATCH);
160
1
        }
161
    }
162
1
    Ok(())
163
2
}